package com.hzw.saas.common.util;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zzl
 * @since 04/09/2021
 */
@Slf4j
public class ObjFieldUtil {

    private static final String SERIAL_PROP_NAME = "serialVersionUID";

    public enum ConvertNull {
        /**
         * 不转换属性
         */
        NONE,
        /**
         * 转换""->null
         */
        EMPTY,
        /**
         * 转换""," "," \r\n"->null
         */
        BLANK,
        ;
    }

    /**
     * 将基本类型的包装类以及String类型的null值替换为默认值 0 或 ""
     *
     * @param t
     * @param ignoreProps
     * @param <T>
     */
    @SafeVarargs
    public static <T> void convertNull2Default(T t, PropertyFunc<T, ?>... ignoreProps) {
        if (Objects.isNull(t)) {
            return;
        }
        try {
            Field[] fields = t.getClass().getDeclaredFields();
            List<String> ignoreEmptyNames = Arrays.stream(ignoreProps).map(LambdaUtil::getFieldName).collect(Collectors.toList());
            for (Field field : fields) {
                if (field.getName().equals(SERIAL_PROP_NAME)) continue;
                if (ignoreEmptyNames.contains(field.getName())) continue;
                if (!isNullField(t, field)) continue;
                if (Objects.equals(field.getType(), Byte.class)) {
                    field.set(t, (byte) 0);
                } else if (Objects.equals(field.getType(), Short.class)) {
                    field.set(t, (short) 0);
                } else if (Objects.equals(field.getType(), Integer.class)) {
                    field.set(t, 0);
                } else if (Objects.equals(field.getType(), Long.class)) {
                    field.set(t, 0L);
                } else if (Objects.equals(field.getType(), Float.class)) {
                    field.set(t, (float) 0);
                } else if (Objects.equals(field.getType(), Double.class)) {
                    field.set(t, (double) 0);
                } else if (Objects.equals(field.getType(), Boolean.class)) {
                    field.set(t, false);
                }
            }
        } catch (ReflectiveOperationException e) {
            log.error("check empty fields error: " + t.getClass().getName(), e);
        }
    }

    /**
     * 是否存在null属性
     *
     * @param t           obj
     * @param ignoreProps 忽略的属性
     * @param <T>         obj type
     * @return 是否存在Null属性
     */
    @SafeVarargs
    public static <T> boolean hasNullFiled(T t, PropertyFunc<T, ?>... ignoreProps) {
        if (Objects.isNull(t))
            return true;
        try {
            Field[] fields = t.getClass().getDeclaredFields();
            List<String> ignoreEmptyNames = Arrays.stream(ignoreProps).map(LambdaUtil::getFieldName).collect(Collectors.toList());
            for (Field field : fields) {
                if (field.getName().equals(SERIAL_PROP_NAME)) continue;
                if (ignoreEmptyNames.contains(field.getName())) continue;
                if (isNullField(t, field)) {
                    return true;
                }
            }
        } catch (ReflectiveOperationException e) {
            log.error("check empty fields error: " + t.getClass().getName(), e);
        }
        return false;
    }

    /**
     * 是否存在null or blank(if String)属性
     *
     * @param t           obj
     * @param ignoreProps 忽略的属性
     * @param <T>         obj type
     * @return 是否存在null or blank(if String)属性
     */
    @SafeVarargs
    public static <T> boolean hasBlankFiled(T t, PropertyFunc<T, ?>... ignoreProps) {
        if (Objects.isNull(t))
            return true;
        try {
            Field[] fields = t.getClass().getDeclaredFields();
            List<String> ignoreEmptyNames = Arrays.stream(ignoreProps).map(LambdaUtil::getFieldName).collect(Collectors.toList());
            for (Field field : fields) {
                if (field.getName().equals(SERIAL_PROP_NAME)) continue;
                if (ignoreEmptyNames.contains(field.getName())) continue;
                if (isBlankField(t, field)) {
                    return true;
                }
            }
        } catch (ReflectiveOperationException e) {
            log.error("check empty fields error: " + t.getClass().getName(), e);
        }
        return false;
    }


    /**
     * 检查bean中属性是否都为null
     *
     * @param t                  bean
     * @param convert            转换类型
     * @param ignoreConvertProps 忽略替换为null的字段
     * @param <T>                class
     * @return true 所有字段为null;false 存在非null字段
     */
    @SafeVarargs
    public static <T> boolean isAllNullFields(T t, ConvertNull convert, PropertyFunc<T, ?>... ignoreConvertProps) {
        if (t == null)
            return true;
        boolean isAllNull = true;
        try {
            Field[] fields = t.getClass().getDeclaredFields();
            List<String> ignoreEmptyNames = Arrays.stream(ignoreConvertProps).map(LambdaUtil::getFieldName).collect(Collectors.toList());
            for (Field field : fields) {
                if (field.getName().equals(SERIAL_PROP_NAME)) continue;
                if (Objects.equals(convert, ConvertNull.NONE) || ignoreEmptyNames.contains(field.getName())) {
                    isAllNull &= isNullField(t, field);
                } else {
                    isAllNull &= isNullField(t, field, convert);
                }
            }
        } catch (Exception e) {
            log.error("check empty fields error: " + t.getClass().getName(), e);
        }
        return isAllNull;
    }

    /**
     * @param t     obj
     * @param field field
     * @param <T>   obj type
     * @return 字段为null?
     * @throws IllegalAccessException e
     */
    private static <T> boolean isNullField(T t, Field field) throws IllegalAccessException {
        field.setAccessible(true);
        return field.get(t) == null;
    }

    /**
     * @param t       obj
     * @param field   field
     * @param convert 指定条件下转换为null
     * @param <T>     obj type
     * @return 字段为为null?
     * @throws IllegalAccessException e
     */
    private static <T> boolean isNullField(T t, Field field, ConvertNull convert) throws IllegalAccessException {
        field.setAccessible(true);
        Object o = field.get(t);
        if (o == null)
            return true;
        if (Objects.equals(convert, ConvertNull.BLANK) ? StrUtil.isBlankIfStr(o) : StrUtil.isEmptyIfStr(o)) {
            field.set(t, null);
            return true;
        }
        return false; // 非null
    }

    /**
     * @param t     obj
     * @param field field
     * @param <T>   obj type
     * @return 字段为null或blank(if String)
     * @throws IllegalAccessException e
     */
    private static <T> boolean isBlankField(T t, Field field) throws IllegalAccessException {
        field.setAccessible(true);
        Object o = field.get(t);
        return StrUtil.isBlankIfStr(o);
    }

}
